#pragma once

#include "vector.h"

namespace g3 {

template <typename T>
class basic_box;

typedef basic_box<double> box;
typedef basic_box<float> boxf;
typedef basic_box<int> boxi;
typedef basic_box<long double> boxl;

template <typename T>
class basic_box {
public:
	T x1, y1, z1, x2, y2, z2;
public:
	basic_box()
		{ }
	basic_box(T _x1, T _y1, T _z1, T _x2, T _y2, T _z2)
		: x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2) { }
	basic_box(const basic_vector<T> & min, const basic_vector<T> & max)
		: x1(min.x), y1(min.y), z1(min.z), x2(max.x), y2(max.y), z2(max.z) { }
	basic_box(const basic_box & b) 
		: x1(b.x1), y1(b.y1), z1(b.z1), x2(b.x2), y2(b.y2), z2(b.z2) { }
	template <class U> 
	basic_box(const basic_box<U> & b) 
		: x1(T(b.x1)), y1(T(b.y1)), z1(T(b.z1)), x2(T(b.x2)), y2(T(b.y2)), z2(T(b.z2)) { }
public:
	basic_vector<T> min() const
		{ return basic_vector<T>(x1, y1, z1); }
	basic_vector<T> max() const
		{ return basic_vector<T>(x2, y2, z2); }
	basic_vector<T> center() const
		{ return (min() + max()) / T(2); }
	bool inside(const basic_vector<T> & v) const
		{ return min() <= v && v < max(); }
	bool empty() const
		{ return !(min() < max()); }
	bool operator == (const basic_box & b) const
		{ return x1 == b.x1 && y1 == b.y1 && z1 == b.z1 && x2 == b.x2 && y2 == b.y2 && z2 == b.z2; }
	bool operator != (const basic_box & b) const
		{ return x1 != b.x1 || y1 != b.y1 || z1 != b.z1 || x2 != b.x2 || y2 != b.y2 || z2 != b.z2; }
	T width() const
		{ return x2 - x1; }
	T height() const
		{ return y2 - y1; }
	T depth() const
		{ return z2 - z1; }
	basic_vector<T> size() const
		{ return basic_vector<T>(width(), height(), depth()); }
	basic_box & normalize() {
		if (x2 < x1)
			std::swap(x1, x2);
		if (y2 < y1)
			std::swap(y1, y2);
		if (z2 < z1)
			std::swap(z1, z2);
		return * this;
	}
	basic_box & inflate(T dx, T dy, T dz) {
		x1 -= dx;
		y1 -= dy;
		z1 -= dz;
		x2 += dx;
		y2 += dy;
		z2 += dz;
		return * this;
	}
	basic_box & inflate(T d)
		{ return inflate(d, d, d); }
	basic_box & inflate(const basic_vector<T> & d)
		{ return inflate(d.x, d.y, d.z); }
	basic_box & include(const basic_vector<T> & v) {
		if (x1 > v.x)
			x1 = v.x;
		if (y1 > v.y)
			y1 = v.y;
		if (z1 > v.z)
			z1 = v.z;
		if (x2 < v.x)
			x2 = v.x;
		if (y2 < v.y)
			y2 = v.y;
		if (z2 < v.z)
			z2 = v.z;
		return * this;
	}
};

} // g3
